﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.Windows.Forms;
using DevExpress.XtraGrid.Views.Grid;
using E4RPDevExpress10;
using E4RPPrintManager10;
using E4RPTools;
using TakeAway.DataSet;

namespace TakeAway
{
    public class Controller
    {
        private SqlConnection _externalConnection;
        private SqlConnection _myConnection;
        private string _cashId;
        private string _sessionId;
        private string _familyString;

        public IWin32Window OwnerWindow { get; set; }

        public PedidoDataSet Request { get; set; }

        public ClienteDataSet Client { get; set; }

        public string Shop { get; set; }

        public string UserName { get; set; }

        public string LogFile { get; set; }

        public bool AllInOne { get; set; }

        #region Private Methods

        private void PublishAndShowErrorMessage(Exception e, string errorMessage)
        {
            ExceptionPublisher.Publish(ConfigurationManager.AppSettings["LogFile"], e);
            ExceptionPublisher.ShowErrorMessage(OwnerWindow, errorMessage, "Takeway");
        }

        private bool LoginMy(string userName, string password)
        {
            var dataSource = ConfigurationManager.AppSettings["FonteDados"];
            var databaseName = ConfigurationManager.AppSettings["NomeBaseDados"];
            var connectionString = "Data Source=" + dataSource + ";Initial Catalog=" + databaseName + ";User ID=" + userName + ";Password=" + password + ";MultipleActiveResultSets=True;";
            try
            {
                _myConnection = new SqlConnection(connectionString);
                _myConnection.Open();
            }
            catch (Exception e)
            {
                PublishAndShowErrorMessage(e, "Não foi possível ligar à base de dados.");
                return false;
            }
            return true;
        }

        private bool LoginExternal(string userName, string password)
        {
            var dataSource = ConfigurationManager.AppSettings["FonteDados"];
            var databaseName = ConfigurationManager.AppSettings["NomeBaseDadosExterna"];
            var connectionString = "Data Source=" + dataSource + ";Initial Catalog=" + databaseName + ";User ID=" + userName + ";Password=" + password + ";MultipleActiveResultSets=True;";
            try
            {
                _externalConnection = new SqlConnection(connectionString);
                _externalConnection.Open();
            }
            catch (Exception e)
            {
                PublishAndShowErrorMessage(e, "Não foi possível ligar à base de dados externa.");
                return false;
            }
            return true;
        }

        private void FixMyConnection()
        {
            if (_myConnection.State == ConnectionState.Open)
                _myConnection.Close();
            _myConnection.Open();
        }

        private void FixExternalConnection()
        {
            if (_externalConnection.State == ConnectionState.Open)
                _externalConnection.Close();
            _externalConnection.Open();
        }

        private bool IsCashOpened()
        {
            FixExternalConnection();
            var sqlTransaction = _externalConnection.BeginTransaction();
            try
            {
                _cashId = PhcDatabase.CashOpened(sqlTransaction);
                sqlTransaction.Commit();
                return _cashId != "";
            }
            catch (Exception e)
            {
                sqlTransaction.Rollback();
                PublishAndShowErrorMessage(e, "Não foi possível validar se a caixa está aberta.");
                return false;
            }
        }

        private void OpenSession()
        {
            FixExternalConnection();
            var sqlTransaction = _externalConnection.BeginTransaction();
            try
            {
                _sessionId = PhcDatabase.OpenSession(sqlTransaction, _cashId);
                sqlTransaction.Commit();
            }
            catch (Exception e)
            {
                sqlTransaction.Rollback();
                PublishAndShowErrorMessage(e, "Não foi possível abrir a sessão.");
            }

        }

        private void OnRequestShow()
        {
            var requestShow = RequestShow;
            if (requestShow != null)
                requestShow(this, new EventArgs());
        }

        private void OnRequestSaved()
        {
            EventHandler<EventArgs> requestSaved = RequestSaved;
            if (requestSaved != null)
                requestSaved(this, new EventArgs());
        }

        private void OnRequestCloseEditors()
        {
            var requestCloseEditors = RequestCloseEditors;
            if (requestCloseEditors != null)
                requestCloseEditors(this, new EventArgs());
        }

        private PedidoDataSet GetRequest(long requestId)
        {
            FixMyConnection();
            var sqlTransaction = _myConnection.BeginTransaction();
            try
            {
                PedidoDataSet dataSet = Database.GetRequest(sqlTransaction, requestId);
                sqlTransaction.Commit();
                return dataSet;
            }
            catch (Exception e)
            {
                sqlTransaction.Rollback();
                PublishAndShowErrorMessage(e, "Não foi possível ler o pedido.");
                return new PedidoDataSet();
            }
        }

        private PedidoDataSet.ItemPedidoRow GetRequestItem(string productReference, string productName)
        {
            foreach (PedidoDataSet.ItemPedidoRow requestItem in Request.ItemPedido.Select("", "", DataViewRowState.CurrentRows))
                if (requestItem.ReferenciaProduto.Trim() == productReference.Trim() && requestItem.NomeProduto.Trim() == productName.Trim())
                    return requestItem;
            return null;
        }

        private PedidoDataSet.ItemPedidoRow GetRequestItem(string productReference)
        {
            foreach (PedidoDataSet.ItemPedidoRow requestItem in Request.ItemPedido.Select("", "", DataViewRowState.CurrentRows))
                if (requestItem.ReferenciaProduto.Trim() == productReference.Trim())
                    return requestItem;
            return null;
        }

        private long GetNextRequestItemId()
        {
            long lastId = 0;
            foreach (var requestItem in Request.ItemPedido)
            {
                var id = requestItem.RowState == DataRowState.Deleted ? Convert.ToInt64(requestItem["IdItem", DataRowVersion.Original]) : requestItem.IdItem;
                if (id < lastId)
                    lastId = id;
            }
            return lastId - 1;
        }

        private ClienteDataSet GetClient(long clientId)
        {
            FixExternalConnection();
            var sqlTransaction = _externalConnection.BeginTransaction();
            try
            {
                var dataSet = PhcDatabase.GetClient(sqlTransaction, clientId);
                sqlTransaction.Commit();
                return dataSet;
            }
            catch (Exception e)
            {
                sqlTransaction.Rollback();
                PublishAndShowErrorMessage(e, "Não foi possível ler o cliente.");
                return new ClienteDataSet();
            }
        }

        private void OnClientShow()
        {
            var clientShow = ClientShow;
            if (clientShow != null)
                clientShow(this, new EventArgs());
        }

        private void OnClientModify()
        {
            var clientModify = ClientModify;
            if (clientModify != null)
                clientModify(this, new EventArgs());
        }

        private bool SaveExternalClientInternal()
        {
            var sqlTransaction = _externalConnection.BeginTransaction();
            try
            {
                PhcDatabase.SaveClient(sqlTransaction, Client.Cliente[0]);
                Request.Pedido[0].Cliente = Client.Cliente[0].Numero;
                sqlTransaction.Commit();
                return true;
            }
            catch (Exception e)
            {
                sqlTransaction.Rollback();
                PublishAndShowErrorMessage(e, "Não foi possível gravar o cliente do pedido.");
                return false;
            }
        }

        private bool SaveExternalRequestInternal()
        {
            var sqlTransaction = _externalConnection.BeginTransaction();
            try
            {
                PhcDatabase.SaveSale(sqlTransaction, _sessionId, Request);
                sqlTransaction.Commit();
                return true;
            }
            catch (Exception e)
            {
                sqlTransaction.Rollback();
                PublishAndShowErrorMessage(e, "Não foi possível gravar o pedido.");
                return false;
            }
        }

        private long SaveRequestInternal()
        {
            var sqlTransaction = _myConnection.BeginTransaction();
            try
            {
                if (Request.Pedido[0].Id <= 0) Request.Pedido[0].CriadoPor = UserName;
                Request.Pedido[0].ActualizadoPor = UserName;
                Database.SaveRequest(sqlTransaction, Request.Pedido[0]);
                Database.UpdateRequestItems(sqlTransaction, Request.ItemPedido);
                sqlTransaction.Commit();
                return Request.Pedido[0].Id;
            }
            catch (Exception e)
            {
                sqlTransaction.Rollback();
                PublishAndShowErrorMessage(e, "Não foi possível gravar o pedido.");
                return -1;
            }
        }

        private ProdutoDataSet GetProduct(string productReference)
        {
            FixExternalConnection();
            var sqlTransaction = _externalConnection.BeginTransaction();
            try
            {
                var family = PhcDatabase.GetProductFamily(sqlTransaction, productReference);
                var category = PhcDatabase.GetProductCategory(sqlTransaction, productReference);
                var dataSet = PhcDatabase.GetProduct(sqlTransaction, family, category, productReference);
                sqlTransaction.Commit();
                return dataSet;
            }
            catch (Exception e)
            {
                sqlTransaction.Rollback();
                PublishAndShowErrorMessage(e, "Não foi possível ler o produto '" + productReference + "'.");
                return new ProdutoDataSet();
            }
        }

        private bool CloseSale(string externalId)
        {
            var sqlTransaction = _externalConnection.BeginTransaction();
            try
            {
                PhcDatabase.CloseSale(sqlTransaction, externalId);
                sqlTransaction.Commit();
                return true;
            }
            catch (Exception e)
            {
                sqlTransaction.Rollback();
                PublishAndShowErrorMessage(e, "Não foi possível fechar o pedido.");
                return false;
            }
        }

        private void UpdateRequestState(long requestId, string state)
        {
            var sqlTransaction = _myConnection.BeginTransaction();
            try
            {
                Database.UpdateRequestState(sqlTransaction, requestId, state, UserName);
                sqlTransaction.Commit();
            }
            catch (Exception e)
            {
                sqlTransaction.Rollback();
                PublishAndShowErrorMessage(e, "Não foi possível actualizar o estado do pedido.");
            }
        }

        private FuncionarioDataSet GetEmployees(string shop)
        {
            var sqlTransaction = _externalConnection.BeginTransaction();
            try
            {
                var dataSet = PhcDatabase.GetEmployees(sqlTransaction, shop);
                sqlTransaction.Commit();
                return dataSet;
            }
            catch (Exception e)
            {
                sqlTransaction.Rollback();
                PublishAndShowErrorMessage(e, "Não foi possível ler a lista de estafetas.");
                return new FuncionarioDataSet();
            }
        }

        private bool AssignExternalEmployeeInternal(string externalId, string employeeName)
        {
            var sqlTransaction = _externalConnection.BeginTransaction();
            try
            {
                PhcDatabase.AssignEmployee(sqlTransaction, externalId, employeeName);
                sqlTransaction.Commit();
                return true;
            }
            catch (Exception e)
            {
                sqlTransaction.Rollback();
                PublishAndShowErrorMessage(e, "Não foi possível atribuir funcionário ao pedido.");
                return false;
            }
        }

        private void AssignEmployeeInternal(long requestId, string employeeName, DateTime? assigned)
        {
            var sqlTransaction = _myConnection.BeginTransaction();
            try
            {
                Database.AssignEmployee(sqlTransaction, requestId, employeeName, assigned, UserName);
                sqlTransaction.Commit();
            }
            catch (Exception e)
            {
                sqlTransaction.Rollback();
                PublishAndShowErrorMessage(e, "Não foi possível atribuir funcionário ao pedido.");
            }
        }

        private ImpressaoDataSet GetPrintDataSet(long requestId)
        {
            var sqlTransaction = _myConnection.BeginTransaction();
            try
            {
                var dataSet = Database.GetPrint(sqlTransaction, requestId);
                sqlTransaction.Commit();
                return dataSet;
            }
            catch (Exception e)
            {
                sqlTransaction.Rollback();
                PublishAndShowErrorMessage(e, "Não foi possível ler a impressao do pedido.");
                return new ImpressaoDataSet();
            }
        }

        private static void PrintSaleFamily(ImpressaoDataSet request, int copyNumber, bool billed)
        {
            request.Pedido[0].CopyName = !billed ? "" : (copyNumber == 1 ? "Original" : "Duplicado");
            var printManager = new PrintManager
            {
                NumberOfCopies = 1,
                PrinterName = ConfigurationManager.AppSettings["Impressora"],
                ReportFile = billed ? "Cliente.rpt" : "Pedido.rpt",
                DataSource = request
            };
            printManager.Print();
        }

        private static List<string> GetDistinctFamilies(ImpressaoDataSet dataSet, string doNotPrintFamily)
        {
            var families = new List<string>();
            foreach (var family in dataSet.FamiliaItemPedido)
            {
                if (family.Familia.ToUpper() == doNotPrintFamily.ToUpper())
                    continue;
                if (families.Contains(family.Familia))
                    continue;
                families.Add(family.Familia);
            }
            return families;
        }

        private static void PrintSaleFamily(System.Data.DataSet request, string family)
        {
            var newRequest = new ImpressaoDataSet();
            newRequest.Merge(request);
            var rowsToDelete = new List<DataRow>();
            foreach (var requestItem in newRequest.ItemPedido)
                if (requestItem.Familia != family)
                    rowsToDelete.Add(requestItem);
            foreach (var requestItem in rowsToDelete)
                requestItem.Delete();
            foreach (var requestItemFamily in newRequest.FamiliaItemPedido)
                if (requestItemFamily.Familia != family)
                    rowsToDelete.Add(requestItemFamily);
            foreach (var requestItem in rowsToDelete)
                requestItem.Delete();
            newRequest.AcceptChanges();
            var printManager = new PrintManager
            {
                NumberOfCopies = 1,
                PrinterName = ConfigurationManager.AppSettings["Impressora"],
                ReportFile = "Restaurante.rpt",
                DataSource = newRequest
            };
            printManager.Print();
        }

        private string GetDocumentTypeName(string shop)
        {
            var sqlTransaction = _externalConnection.BeginTransaction();
            try
            {
                var documentTypeName = PhcDatabase.GetDocumentTypeName(sqlTransaction, shop);
                sqlTransaction.Commit();
                return documentTypeName;
            }
            catch (Exception e)
            {
                sqlTransaction.Rollback();
                PublishAndShowErrorMessage(e, "Não foi possível ler a listagem Z.");
                return "";
            }
        }

        private ListagemZDataSet GetListagemZPrintDataSet()
        {
            var sqlTransaction = _externalConnection.BeginTransaction();
            try
            {
                var dataSet = PhcDatabase.GetListagemZ(sqlTransaction, DateTime.Now, Shop);
                sqlTransaction.Commit();
                return dataSet;
            }
            catch (Exception e)
            {
                sqlTransaction.Rollback();
                PublishAndShowErrorMessage(e, "Não foi possível ler a listagem Z.");
                return new ListagemZDataSet();
            }
        }

        #endregion

        #region Public Event

        public event EventHandler<EventArgs> ClientShow;
        public event EventHandler<EventArgs> ClientModify;
        public event EventHandler<EventArgs> ProductFamilyChanged;
        public event EventHandler<EventArgs> RequestCloseEditors;
        public event EventHandler<EventArgs> RequestShow;
        public event EventHandler<EventArgs> RequestSaved;
        public event EventHandler<EventArgs> RequestsRefreshed;

        #endregion

        public bool Login(string userName, string password)
        {
            UserName = userName;
            return LoginMy(userName, password) && LoginExternal(userName, password);
        }

        public bool IsLicenseValid()
        {
            FixMyConnection();
            return E4RPLicense.License.ValidateDatabase(_myConnection, Product.Takeaway, 2, null, LogFile);
        }

        public void SetShop(string shopCode)
        {
            AllInOne = false;
            Shop = "Indefinida";
            if(string.IsNullOrEmpty(shopCode))
                return;
            //int code = Convert.ToInt32(shopCode);
            //if ("CENTRAL".GetHashCode() == code)
            //{
            //    Shop = "";
            //    return;
            //}
            //LojaDataSet dataSet = GetShops();
            //foreach(var shop in dataSet.Loja)
            //{
            //    if (shop.Id.GetHashCode() != code)
            //        continue;
            //    Shop = shop.Id;
            //}
            if ("COMPLETO" == shopCode)
            {
                Shop = "";
                AllInOne = true;
                return;
            }
            if ("CENTRAL" == shopCode)
            {
                Shop = "";                
                return;
            }
            LojaDataSet dataSet = GetShops();
            foreach (var shop in dataSet.Loja)
            {
                if (shop.Id != shopCode)
                    continue;
                Shop = shop.Id;
            }
        }

        public void CloseConnections()
        {
            _myConnection.Close();
            _externalConnection.Close();
        }

        public ZonaDataSet GetZones()
        {
            FixExternalConnection();
            var sqlTransaction = _externalConnection.BeginTransaction();
            try
            {
                var data = PhcDatabase.GetZones(sqlTransaction, Shop);
                sqlTransaction.Commit();
                return data;
            }
            catch (Exception e)
            {
                sqlTransaction.Rollback();
                PublishAndShowErrorMessage(e, "Não foi possível ler as zonas.");
                return new ZonaDataSet();
            }
        }

        public ProdutoDataSet GetProductsOfShop(string shop)
        {
            FixExternalConnection();
            var sqlTransaction = _externalConnection.BeginTransaction();
            try
            {
                var data = PhcDatabase.GetProductsOfShop(sqlTransaction, shop);
                sqlTransaction.Commit();
                return data;
            }
            catch (Exception e)
            {
                sqlTransaction.Rollback();
                PublishAndShowErrorMessage(e, string.Format("Não foi possível ler os podutos da loja '{0}'.", shop));
                return new ProdutoDataSet();
            }
        }

        public TipoLocalDataSet GetLocalType()
        {
            var dataSet = new TipoLocalDataSet();
            dataSet.TipoLocal.AddTipoLocalRow(0, "Casa");
            dataSet.TipoLocal.AddTipoLocalRow(1, "Emprego");
            dataSet.TipoLocal.AddTipoLocalRow(2, "Férias");
            return dataSet;
        }
        
        public string ProductFamilyString
        {
            get { return _familyString; }
            set
            {
                _familyString = value;
                var familyChanged = ProductFamilyChanged;
                if (familyChanged != null)
                    familyChanged(this, new EventArgs());
            }
        }

        public bool HasExternalStock(string reference, bool? isService, string productName)
        {
            if (isService ?? false)
                return true;
            FixExternalConnection();
            var sqlTransaction = _externalConnection.BeginTransaction();
            try
            {
                decimal stockCount = PhcDatabase.Stock(sqlTransaction, reference);
                PedidoDataSet.ItemPedidoRow requestItem = GetRequestItem(reference, productName);
                decimal quantity = requestItem == null ? 0 : requestItem.Quantidade;
                return stockCount > quantity;            
            }
            catch (Exception e)
            {
                sqlTransaction.Rollback();
                PublishAndShowErrorMessage(e, "Não foi validar se o produto '" + reference + "' tem stock.");
                return true;
            }
        }

        public ProdutoDataSet GetProduct(string family, string category, string productReference)
        {
            FixExternalConnection();
            var sqlTransaction = _externalConnection.BeginTransaction();
            try
            {
                ProdutoDataSet dataSet = PhcDatabase.GetProduct(sqlTransaction, family, category, productReference);
                sqlTransaction.Commit();
                return dataSet;
            }
            catch (Exception e)
            {
                sqlTransaction.Rollback();
                PublishAndShowErrorMessage(e, "Não foi possível ler o produto '" + productReference + "'.");
                return new ProdutoDataSet();
            }
        }

        public PedidoDataSet GetOnHoldRequests()
        {
            FixMyConnection();
            var sqlTransaction = _myConnection.BeginTransaction();
            try
            {
                var states = new List<RequestState> {RequestState.Novo, RequestState.Associado, RequestState.Alterado};
                var dataSet = Database.GetRequestsByState(sqlTransaction, states, Shop, DateTime.Now, DateTime.Now);
                sqlTransaction.Commit();
                return dataSet;
            }
            catch (Exception e)
            {
                sqlTransaction.Rollback();
                PublishAndShowErrorMessage(e, "Não foi possível ler a lista de pedidos pendentes.");
                return new PedidoDataSet();
            }
        }

        public PedidoDataSet GetBilledRequests()
        {
            FixMyConnection();
            var sqlTransaction = _myConnection.BeginTransaction();
            try
            {
                var states = new List<RequestState> {RequestState.Facturado};
                var dataSet = Database.GetRequestsByState(sqlTransaction, states, Shop, DateTime.Now, DateTime.Now);
                sqlTransaction.Commit();
                return dataSet;
            }
            catch (Exception e)
            {
                sqlTransaction.Rollback();
                PublishAndShowErrorMessage(e, "Não foi possível ler a lista de pedidos atribuídos.");
                return new PedidoDataSet();
            }
        }

        public PedidoDataSet GetAllRequests(DateTime beginDate, DateTime endDate)
        {
            FixMyConnection();
            var sqlTransaction = _myConnection.BeginTransaction();
            try
            {
                var states = new List<RequestState> { RequestState.Alterado, RequestState.Associado, RequestState.Cancelado, RequestState.Facturado, RequestState.Fechado, 
                    RequestState.Novo,RequestState.Substituido};
                var dataSet = Database.GetRequestsByState(sqlTransaction, states, "", beginDate, endDate);
                sqlTransaction.Commit();
                return dataSet;
            }
            catch (Exception e)
            {
                sqlTransaction.Rollback();
                PublishAndShowErrorMessage(e, "Não foi possível ler a lista de todos pedidos.");
                return new PedidoDataSet();
            }
        }

        public FuncionarioDataSet GetAssignedEmployees(string shop)
        {
            FixMyConnection();
            var sqlTransaction = _myConnection.BeginTransaction();
            try
            {
                var dataSet = Database.GetAssignedEmployees(sqlTransaction, shop);
                sqlTransaction.Commit();
                return dataSet;
            }
            catch (Exception e)
            {
                sqlTransaction.Rollback();
                PublishAndShowErrorMessage(e, "Não foi possível ler a lista de estafetas atribuidos a pedidos.");
                return new FuncionarioDataSet();
            }
        }

        public FuncionarioDataSet GetFreeEmployees(string shop)
        {
            var allDataSet = GetEmployees(shop);
            var assignedDataSet = GetAssignedEmployees(shop);
            var dataSet = new FuncionarioDataSet();
            foreach (FuncionarioDataSet.FuncionarioRow employee in allDataSet.Funcionario)
            {
                if (assignedDataSet.Funcionario.FindByNome(employee.Nome) != null)
                    continue;
                dataSet.Funcionario.AddFuncionarioRow(employee.Nome);
            }
            return dataSet;
        }

        public bool ExistsOpenedRequest(IWin32Window owner, string action)
        {
            if (Request == null)
                return true;
            OnRequestCloseEditors();
            if (Request.Pedido[0].Id < 0 && Request.ItemPedido.Count == 0)
                return true;
            if (!Request.HasChanges())
                return true;
            var result = MessageBox.Show(owner, "Existe um pedido por tratar." + Environment.NewLine + action, "Pedido",
                                         MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2);
            return result == DialogResult.Yes;
        }

        public void ShowRequest(long requestId)
        {
            Request = GetRequest(requestId);            
            OnRequestShow();
            Client = GetClient(Request.Pedido[0].Cliente);
            OnClientShow();
        }

        public void NewRequest()
        {
            Request = new PedidoDataSet();
            var request = Request.Pedido.NewPedidoRow();
            request.Id = -1;
            request.Nome = "";
            request.Estado = RequestState.Novo.ToString();
            request.Valor = 0;
            request.ValorPago = 0;
            request.TipoLocal = (int) LocalType.Home;
            request.Emissao = DateTime.Now;
            request.Versao = 1;
            Request.Pedido.AddPedidoRow(request);
            OnRequestShow();
        }

        public void AddProduct(string family, string category, string productReference, string productName)
        {
            var requestItem = GetRequestItem(productReference, productName);
            if (requestItem != null)
            {
                requestItem.Quantidade += 1;
                requestItem.ValorTotal = requestItem.Quantidade * requestItem.Valor;
            }
            else
            {
                var product = GetProduct(family, category, productReference);
                requestItem = Request.ItemPedido.NewItemPedidoRow();
                requestItem.Id = Request.Pedido[0].Id;
                requestItem.IdItem = GetNextRequestItemId();
                requestItem.Familia = product.Produto[0].Familia;
                requestItem.ReferenciaProduto = product.Produto[0].Referencia;
                requestItem.NomeProduto = productName;
                requestItem.Quantidade = 1;
                requestItem.Valor = product.Produto[0].Valor;
                requestItem.PercentagemIva = product.Produto[0].PercentagemIva;
                requestItem.ValorTotal = product.Produto[0].Valor;
                requestItem.Stock = product.Produto[0].Stock;
                if (!product.Produto[0].IsPrecoTabelaNull()) requestItem.PrecoTabela = product.Produto[0].PrecoTabela;
                Request.ItemPedido.AddItemPedidoRow(requestItem);
            }
            UpdateRequestValue();
        }

        public void DeleteRequestItem(string productReference, string productName)
        {
            var requestItem = GetRequestItem(productReference, productName);
            if(requestItem == null)
                return;
            requestItem.Delete();
            UpdateRequestValue();
        }

        public Result IsRequestSaveValid()
        {
            return Validator.ValidateSave(Request, Client);
        }
        
        public bool SaveRequest(bool send)
        {
            //if (!IsCashOpened())
            //{
            //    MessageBox.Show(OwnerWindow, "A caixa está fechada." + Environment.NewLine +
            //                    "Por favor, abra a caixa e tente de novo.", "Gravar", MessageBoxButtons.OK, MessageBoxIcon.Information);
            //    return false;
            //}
            //OpenSession();
            if (!SaveExternalClientInternal())
                return false;
            // TODO Deve actualizar o numero de cliente no interface
            if (SaveRequestInternal() < 0)
                return false;
            if (send)
                SendRequest(Request.Pedido[0].Id);
            else
            {
                OnRequestSaved();
                NewClient();
                NewRequest();                            
            }
            return true;
        }

        public Result IsRequestBillValid()
        {
            return Validator.ValidateBill(Request, Client, AllInOne);
        }

        public bool BillRequest(string employeeName)
        {
            if (!IsCashOpened())
            {
                MessageBox.Show(OwnerWindow, "A caixa está fechada." + Environment.NewLine +
                                "Por favor, abra a caixa e tente de novo.", "Gravar", MessageBoxButtons.OK, MessageBoxIcon.Information);
                return false;
            }
            OpenSession();
            Request.Pedido[0].Estado = RequestState.Facturado.ToString();
            if (!SaveExternalClientInternal())
                return false;
            if (!SaveExternalRequestInternal())
                return false;
            if (SaveRequestInternal() < 0)
                return false;
            AssignEmployee(Request.Pedido[0].Id, employeeName, DateTime.Now, Request.Pedido[0].ExternoId);
            if (ConfigurationManager.AppSettings["ImprimirAposFacturar"] == "1")
                PrintRequest(Request.Pedido[0].Id, true);
            OnRequestSaved();
            NewClient();
            NewRequest();            
            return true;
        }


        public bool SaveAndBillRequest(string employeeName)
        {
            if (!IsCashOpened())
            {
                MessageBox.Show(OwnerWindow, "A caixa está fechada." + Environment.NewLine +
                                "Por favor, abra a caixa e tente de novo.", "Gravar", MessageBoxButtons.OK, MessageBoxIcon.Information);
                return false;
            }
            OpenSession();
            Request.Pedido[0].Estado = RequestState.Facturado.ToString();
            if (!SaveExternalClientInternal())
                return false;
            if (!SaveExternalRequestInternal())
                return false;
            if (SaveRequestInternal() < 0)
                return false;
            Request.AcceptChanges();
            Request.Pedido[0].SetModified();
            if (SaveRequestInternal() < 0)
                return false;
            AssignEmployee(Request.Pedido[0].Id, employeeName, DateTime.Now, Request.Pedido[0].ExternoId);
            if (ConfigurationManager.AppSettings["ImprimirAposFacturar"] == "1")
                PrintRequest(Request.Pedido[0].Id, true);
            OnRequestSaved();
            NewClient();
            NewRequest();            
            return true;
        }
        
        public void OnRefreshRequests()
        {
            var eventHandler = RequestsRefreshed;
            if (eventHandler != null)
                eventHandler(this, new EventArgs());
        }

        public void UpdateRequestValue()
        {
            decimal requestValue = 0;
            foreach (PedidoDataSet.ItemPedidoRow requestItem in Request.ItemPedido.Select("", "", DataViewRowState.CurrentRows))
                requestValue += requestItem.ValorTotal;
            Request.Pedido[0].Valor = requestValue;
        }

        public ClienteDataSet GetClients(string name, string phone, string fiscalNumber, string street)
        {
            FixExternalConnection();
            var sqlTransaction = _externalConnection.BeginTransaction();
            try
            {
                var dataSet = PhcDatabase.GetClients(sqlTransaction, name, phone, fiscalNumber, street);
                sqlTransaction.Commit();
                return dataSet;
            }
            catch (Exception e)
            {
                sqlTransaction.Rollback();
                PublishAndShowErrorMessage(e, "Não foi possível ler os clientes.");
                return new ClienteDataSet();
            }
        }

        public void NewClient()
        {
            Client = new ClienteDataSet();
            var client = Client.Cliente.NewClienteRow();
            client.Numero = -1;
            Client.Cliente.AddClienteRow(client);
            OnClientShow();
        }

        public void NewClient(bool modify, string name, string phone, string fiscalNumber, string street)
        {
            Client = new ClienteDataSet();
            var client = Client.Cliente.NewClienteRow();
            client.Numero = -1;
            client.Nome = name;
            client.Telefone = phone;
            client.Contribuinte = fiscalNumber;
            client.MoradaCasa = street;
            Client.Cliente.AddClienteRow(client);
            if (modify)
                OnClientModify();
            else
                OnClientShow();
        }

        public void ShowClient(bool modify, long clientId)
        {
            Client = GetClient(clientId);
            if (modify)
                OnClientModify();
            else
                OnClientShow();
        }

        public void RemoveZoneSaleItem(string productReference)
        {
            if (productReference == "" || Request.ItemPedido.Count == 0)
                return;
            var requestItem = GetRequestItem(productReference);
            if (requestItem == null)
                return;
            requestItem.Delete();
            UpdateRequestValue();
        }

        public void AddZoneSaleItem(string productReference)
        {
            if (productReference == "")
            {
                MessageBox.Show(OwnerWindow, "A zona seleccionada não tem taxa de serviço associada.", "Zona", MessageBoxButtons.OK, MessageBoxIcon.Information);
                return;
            }
            ProdutoDataSet product = GetProduct(productReference);
            if (product == null || product.Produto.Count == 0)
            {
                MessageBox.Show(OwnerWindow, "A zona seleccionada tem taxa de serviço mal parametrizada.", "Zona", MessageBoxButtons.OK, MessageBoxIcon.Information);
                return;
            }
            AddProduct(product.Produto[0].Familia, product.Produto[0].Categoria, product.Produto[0].Referencia, product.Produto[0].IsDescricaoNull() ? "" : product.Produto[0].Descricao);            
        }

        public void CloseRequest(PedidoDataSet.PedidoRow request)
        {
            if (!CloseSale(request.ExternoId))
                return;
            UpdateRequestState(request.Id, RequestState.Fechado.ToString());
            OnRequestSaved();             
        }

        public void AssignEmployee(long requestId, string employeeName, DateTime? assigned, string externalId)
        {
            if(externalId != "" && !AssignExternalEmployeeInternal(externalId, employeeName))
                return;
            AssignEmployeeInternal(requestId, employeeName, assigned);
            OnRequestSaved();
        }
        
        public void PrintRequest(long requestId, bool billed)
        {
            var dataSet = GetPrintDataSet(requestId);
            dataSet.Pedido[0].ExternoSerie = GetDocumentTypeName(dataSet.Pedido[0].Loja);
            PrintSaleFamily(dataSet, 1, billed);
            if(!billed)
                return;
            PrintSaleFamily(dataSet, 2, true);
            if (ConfigurationManager.AppSettings["ImprimirRestaurante"] != "1")
                return;
            string doNotPrintFamily = ConfigurationManager.AppSettings["NaoImprimirFamilia"];
            var families = GetDistinctFamilies(dataSet, doNotPrintFamily);
            foreach (var family in families)
                PrintSaleFamily(dataSet, family);
        }


        public void SendRequest(long requestId)
        {
            UpdateRequestState(requestId, RequestState.Associado.ToString());
            OnRequestSaved();
            NewClient();
            NewRequest();            
        }

        public void ChangeRequest(long requestId)
        {
            UpdateRequestState(requestId, RequestState.Cancelado.ToString());
            Request = GetRequest(requestId);
            Request.Pedido[0].Id = -1;
            Request.Pedido[0].Estado = RequestState.Alterado.ToString();
            Request.Pedido[0].Versao = 2;
            Request.Pedido[0].AcceptChanges();
            Request.Pedido[0].SetAdded();
            foreach(PedidoDataSet.ItemPedidoRow requestItem in Request.ItemPedido)
            {
                requestItem.AcceptChanges();
                requestItem.SetAdded();
            }
            long id = SaveRequestInternal();
            if (id == -1)
                return;
            //OnRequestSaved();
            //NewClient();
            //NewRequest();            
            ShowRequest(id);            
        }

        public void CancelRequest(long requestId)
        {
            UpdateRequestState(requestId, RequestState.Cancelado.ToString());
            OnRequestSaved();
            NewClient();
            NewRequest();            
        }

        public LojaDataSet GetShops()
        {
            FixMyConnection();
            var sqlTransaction = _myConnection.BeginTransaction();
            try
            {
                var dataSet = Database.GetShops(sqlTransaction);
                sqlTransaction.Commit();
                return dataSet;
            }
            catch (Exception e)
            {
                sqlTransaction.Rollback();
                PublishAndShowErrorMessage(e, "Não foi possível ler a lista de lojas.");
                return new LojaDataSet();
            }
        }

        public void PrintListagemZ()
        {
            //if (!IsCashOpened())
            //{
            //    MessageBox.Show(OwnerWindow, "A caixa está fechada." + Environment.NewLine +
            //                    "Por favor, abra a caixa e tente de novo.", "Listagem Z", MessageBoxButtons.OK, MessageBoxIcon.Information);
            //    return;
            //}
            var dataSet = GetListagemZPrintDataSet();
            var printManager = new PrintManager
                                   {
                                       NumberOfCopies = 1,
                                       PrinterName = ConfigurationManager.AppSettings["ImpressoraRelatorio"],
                                       ReportFile = "ListagemZ.rpt",
                                       DataSource = dataSet
                                   };
            printManager.Print();
        }

        #region ListProfile

        public string LoadLayout(GridView gridView, string list)
        {
            FixMyConnection();
            return ListProfile.LoadProfile(_myConnection, OwnerWindow, LogFile, Product.Takeaway.ToString(), gridView, list + UserName);
        }

        public string SaveLayout(IWin32Window owner, string list, string xmlLayout)
        {
            FixMyConnection();
            return ListProfile.SaveProfile(_myConnection, OwnerWindow, LogFile, Product.Takeaway.ToString(), list + UserName, xmlLayout);
        }

        #endregion

    }
}
